home *** CD-ROM | disk | FTP | other *** search
/ Practical Algorithms for Image Analysis / Practical Algorithms for Image Analysis.iso / TARFILE.GZ / tarfile / util / linux / tofrodos-1.1 / tofrodos.c < prev    next >
C/C++ Source or Header  |  1999-09-11  |  16KB  |  562 lines

  1. /*
  2.     tofrodos.c    Converts text files between DOS and Unix formats.
  3.     Copyright (c) 1996 by Christopher S L Heng. All rights reserved.
  4.  
  5.     $Id: tofrodos.c 1.3 1996/12/16 17:49:13 chris Exp $
  6. */
  7.  
  8. /* this should always be first */
  9. #include "config.h"
  10.  
  11. /* standard headers */
  12. #include <signal.h>    /* signal() */
  13. #include <stdio.h>    /* FILE functions */
  14. #include <stdlib.h>    /* EXIT_SUCCESS */
  15. #include <string.h>    /* strrchr(), strlen(), strcpy(), strcat() */
  16. #include <sys/stat.h>    /* stat() */
  17.  
  18. /* conditionally included headers */
  19. #if defined(MSDOS)
  20. #include <fcntl.h>    /* O_BINARY */
  21. #include <io.h>        /* chmod(), setmode(), isatty() */
  22. #endif
  23.  
  24. #if defined(HAVE_GETOPT_H)
  25. #include <getopt.h>    /* optind */
  26. #endif
  27.  
  28. #if defined(HAVE_MKTEMP_H)
  29. #include MKTEMP_HEADER
  30. #endif
  31.  
  32. #if defined(HAVE_UNISTD_H)
  33. #include <unistd.h>    /* chmod(), mktemp(), isatty() */
  34. #endif
  35.  
  36. /* our headers */
  37. #include "emsg.h"
  38. #include "tofrodos.h"
  39. #include "utility.h"
  40. #include "version.h"
  41.  
  42. /* macros */
  43. #define    BAKEXT        ".bak"    /* backup file extension */
  44. #define    DIRSLASH    '/'    /* works for both MSDOS and Unix */
  45. #define    DIRSLASHSTR    "/"
  46. #define    MKTEMP_TEMPL    "XXXXXX"
  47. #define    NEWBUFSIZ    16384    /* buffer size for the files */
  48.  
  49. /* conditional macros */
  50. #if defined(MSDOS)
  51. #if !defined(_MAX_DIR) || (_MAX_DIR < 260)    /* MAXDIRSIZE */
  52. #define MAXDIRSIZE    260
  53. #else
  54. #define    MAXDIRSIZE    _MAX_DIR
  55. #endif
  56. #if !defined(_MAX_NAME) || (_MAX_NAME < 260)    /* MAXFILESIZE */
  57. #define MAXFILESIZE    260
  58. #else
  59. #define    MAXFILESIZE    _MAX_NAME
  60. #endif
  61. #if !defined(_MAX_PATH) || (_MAX_PATH < 260)    /* MAXPATHSIZE */
  62. #define    MAXPATHSIZE    260
  63. #else
  64. #define    MAXPATHSIZE    _MAX_PATH
  65. #endif
  66. #endif
  67.  
  68. #if defined(MSDOS)
  69. #define    INFILEMODE    "rb"
  70. #define    OUTFILEMODE    "wb"
  71. #else
  72. #define    INFILEMODE    "r"
  73. #define    OUTFILEMODE    "w"
  74. #endif
  75.  
  76. #if !defined(MSDOS)
  77. #define    CURRENTDIR    "./"
  78. #endif
  79.  
  80. /* global variables */
  81. int abortonerr ; /* 1 if should abort when there is error in any file */
  82.         /* in a list of files, 0 carry on (default) */
  83. int alwaysconvert ; /* convert all \r\n to \r\r\n when direction */
  84.         /* is UNIXTODOS, and delete all \r when direction is */
  85.         /* DOSTOUNIX */
  86. int direction = DEFDIRECTION ; /* UNIXTODOS or DOSTOUNIX */
  87. int forcewrite ; /* convert even if file is not writeable */
  88. char * progname = VERSN_PROGNAME ;/* name of binary (ie, argv[0]) */
  89. int overwrite = 1 ;    /* 1 = overwrite original file, 0 = make backup */
  90. int verbose ;
  91.  
  92. /* local variables */
  93. static char * infilename = "stdin" ;
  94. static unsigned short origfilemode ;    /* file mode of original file */
  95. static FILE * tempfp ;
  96. static char * tempfilename ;
  97.  
  98. /* local functions */
  99. static int checkmode ( char * filename );
  100. static int convert ( FILE * infp, FILE * outfp );
  101. static int openandconvert ( char * filename );
  102.  
  103. /*
  104.     main
  105.  
  106.     tofrodos converts ASCII text files to/from a DOS CR-LF deliminated
  107.     form from/to a Unix LF deliminated form.
  108.  
  109.     Usage: tofrodos [options] [file...]
  110.  
  111.     Exit codes:
  112.         EXIT_SUCCESS    success    (stdlib.h)
  113.         EXIT_ERROR    error    (tofrodos.h)
  114. */
  115. int main ( int argc, char ** argv )
  116. {
  117.     /* initialise and parse the options */
  118.     if (init( argv[0] ) || parseargs( argc, argv ))
  119.         return EXIT_ERROR ;
  120.  
  121.     /* check if we are to convert from stdin */
  122.     if (argc == optind) {
  123.         if (isatty( fileno( stdin ) )) {
  124.         /* stdin must be redirected else you should supply a */
  125.         /* filename. */
  126.         fprintf( stdout, EMSG_NOFILENAME, progname );
  127.         return EXIT_ERROR ;
  128.         }
  129.         /* otherwise stdin has been redirected */
  130. #if defined(MSDOS)
  131.         /* need to make sure the input and output files are binary */
  132.         /* on MSDOS */
  133.         setmode( fileno( stdin ), O_BINARY );
  134.         setmode( fileno( stdout ), O_BINARY );
  135. #endif
  136.         return openandconvert( NULL );
  137.     }
  138.  
  139.     /* if we reach here, we have a (list?) of files to convert */
  140.     /* (ignore stdin) */
  141.     while (optind < argc) {
  142.         if (verbose)
  143.         puts( argv[optind] );
  144.         if (openandconvert( argv[optind] ) != 0 && abortonerr)
  145.         return EXIT_ERROR ;
  146.         optind++ ;
  147.     }
  148.  
  149.     return EXIT_SUCCESS ;
  150. }
  151.  
  152. /*
  153.     sighandler
  154.  
  155.     Handles SIGINT and SIGTERM. Prints a message, closes and
  156.     deletes the temporary files and quits with EXIT_ERROR.
  157.  
  158.         It never returns (and Watcom C knows it).
  159. */
  160. void sighandler ( int sig )
  161. {
  162.     /* restore signal handler, in case we have the old unsafe */
  163.         /* behaviour */
  164.     signal( sig, sighandler );
  165.  
  166.     /* print error message if verbose */
  167.     if (verbose)
  168.         fprintf( stderr, EMSG_SIGNAL, progname );
  169.  
  170.     /* close the temporary file and delete it */
  171.     if (tempfp != NULL) {
  172.         fclose( tempfp );
  173.         tempfp = NULL ;
  174.     }
  175.     if (tempfilename != NULL) {
  176.         remove( tempfilename );
  177.                 tempfilename = NULL ;
  178.     }
  179.  
  180.         exit( EXIT_ERROR );
  181. }
  182.  
  183. /* ---------------------------- local functions --------------------- */
  184. /*
  185.     checkmode
  186.  
  187.     Checks that the file we are supposed to convert is indeed
  188.     writeable. We don't really need for it to be writeable, since
  189.     we actually open a new file and eventually delete the current
  190.     file.
  191.  
  192.     However, if a file is marked not-writeable, we should at least
  193.     respect the user's choice and abort unless he flags the
  194.     forcewrite flag.
  195.  
  196.     At the same time we also save the current mode of the file
  197.     so that we can set the converted file to the same mode. The
  198.     value is saved in origfilemode.
  199.  
  200.     Returns: 0 on success, -1 on error.
  201.  
  202.     If -1 is returned, it could mean one of few things:
  203.     1) some component of the path was not valid (directory or the file
  204.     itself) (DOS/Unix) or search permission was denied (Unix)
  205.     2) the file is not readable
  206.     3) the file is not writeable and forcewrite is zero.
  207.     An error message is displayed on error.
  208. */
  209. static int checkmode ( char * filename )
  210. {
  211.     struct stat statbuf ;
  212.  
  213.     /* get the file information */
  214.     if (stat( filename, &statbuf )) {
  215.         /* couldn't stat the file. */
  216.         fprintf( stdout, EMSG_ACCESSFILE, progname, filename );
  217.         return -1 ;
  218.     }
  219.     /* save the mode */
  220.     origfilemode = statbuf.st_mode ;
  221.     /* check if file can be read - this is actually redundant for */
  222.     /* DOS systems. */
  223.     if (!(origfilemode & S_IRUSR)) { /* not readable */
  224.         fprintf( stdout, EMSG_NOTREADABLE, progname, filename );
  225.         return -1 ;
  226.     }
  227.     /* check if file can be written to, if forcewrite is 0 */
  228.     if (!forcewrite && !(origfilemode & S_IWUSR)) { /* not writeable */
  229.         fprintf( stdout, EMSG_NOTWRITEABLE, progname, filename );
  230.         return -1 ;
  231.     }
  232.     return 0 ;
  233. }
  234.  
  235. /*
  236.     convert
  237.  
  238.     Does the actual work of converting infp to outfp.
  239.  
  240.     If direction is DOSTOUNIX, "\r\n" pairs will be converted to
  241.     '\n'. However, standalone '\r' without a '\n' immediately
  242.     following will not be eliminated unless alwaysconvert is
  243.     nonzero.
  244.  
  245.     If direction is UNIXTODOS, '\n' will be converted to "\r\n".
  246.     However "\r\n" pairs are not converted to '\r\r\n' unless
  247.     alwaysconvert is nonzero.
  248.  
  249.         Returns 0 on success, -1 on error.
  250. */
  251. static int convert ( FILE * infp, FILE * outfp )
  252. {
  253.     int prevch ;
  254.     int c ;
  255.  
  256.     /* actually it is very simple to do the conversion in DOS */
  257.     /* because the stdio library does this work automatically for */
  258.     /* us. But because we want this program to work on Linux as */
  259.     /* well, a little bit of work stands before us (but only a little). */
  260.  
  261.     prevch = EOF ;
  262.  
  263.     if (direction == UNIXTODOS) {
  264.         /* basically we convert all newlines to "\r\n" unless */
  265.         /* the file is already in "\r\n" format. The problem here */
  266.         /* is when you have special situations like a Unix */
  267.         /* text file with lines that have a '\r' just */
  268.         /* before a '\n'. These lines will */
  269.         /* not be converted to "\r\r\n" since the function */
  270.                 /* below assumes the line has already been converted. */
  271.                 /* To force the conversion of all \n to \r\n regardless */
  272.         /* of preceding characters, set alwaysconvert to 1. */
  273.         while ( (c = getc( infp )) != EOF ) {
  274.             if (c == '\n' && (!alwaysconvert && prevch != '\r')) {
  275.                 if (putc( '\r', outfp ) == EOF)
  276.                     break ;
  277.             }
  278.                         /* always emit the current character */
  279.             if (putc( c, outfp ) == EOF)
  280.                             break ;
  281.             prevch = c ;                             
  282.                 }
  283.     }
  284.     else if (direction == DOSTOUNIX) {
  285.         if (!alwaysconvert) {
  286.         /* basically we withhold emitting any '\r' until we */
  287.         /* are sure that the next character is not a '\n'. */
  288.         /* If it is not, we emit the '\r', if it is, we */
  289.                 /* only emit the '\n'. */
  290.         while ( (c = getc( infp )) != EOF ) {
  291.             if (prevch == '\r') {
  292.                 /* '\r' is a special case because we don't */
  293.                 /* emit a '\r' until the next character */
  294.                 /* has been read */
  295.                 if (c == '\n') { /* a "\r\n" pair */
  296.                     /* discard previous '\r' and */
  297.                     /* just put the '\n' */
  298.                     if (putc( c, outfp ) == EOF)
  299.                         break ;
  300.                 }
  301.                 else {    /* prevch was a standalone '\r' */
  302.                                     /* emit the standalone '\r' */
  303.                     if (putc( '\r', outfp ) == EOF)
  304.                         break ;
  305.                     /* emit the current character if */
  306.                                         /* it is not a '\r' */
  307.                     if (c != '\r') {
  308.                         if (putc( c, outfp ) == EOF)
  309.                             break ;
  310.                                         }
  311.                 }
  312.             }
  313.             else { /* prevch was not '\r' */
  314.                 /* emit current character if it is not */
  315.                 /* a '\r' */
  316.                 if (c != '\r') {
  317.                     if (putc( c, outfp ) == EOF)
  318.                         break ;
  319.                 }
  320.             }
  321.             prevch = c ;
  322.         }
  323.         }    /* alwaysconvert == 0 */
  324.         else { /* eliminate all '\r' */
  325.         while ((c = getc( infp )) != EOF) {
  326.             if (c != '\r') {
  327.                 if (putc( c, outfp ) == EOF)
  328.                     break ;
  329.             }
  330.             /* else skip all carriage returns */
  331.         }
  332.         }
  333.     }
  334.     else {
  335.         fprintf( stderr, EMSG_INTERNAL, progname, EINTNL_DIRECTION );
  336.         return -1 ;
  337.     }
  338.  
  339.     /* if we reach here, either we've reached an EOF or an error */
  340.     /* occurred. */
  341.     if (!feof( infp )) { /* error */
  342.         fprintf( stderr, EMSG_CONVERT, progname, infilename );
  343.                 return -1 ;
  344.     }
  345.     return 0 ;
  346. }
  347.  
  348. /*
  349.     openandconvert
  350.  
  351.     Called to open the files and convert the contents. If you want
  352.     it to convert stdin to stdout, call it with NULL as the filename
  353.     argument; otherwise pass the function the name of the input file.
  354.  
  355.     Returns: 0 on success, -1 on error. Error messages will be
  356.         displayed on error before returning.
  357. */
  358. static int openandconvert ( char * filename )
  359. {
  360.     FILE * infp ;
  361.         FILE * outfp ;
  362.     int err ;
  363.     char * bakfilename ;
  364. #if defined(MSDOS)
  365.     char drv[_MAX_DRIVE];
  366.     char dir[MAXDIRSIZE];
  367.     char fname[MAXFILESIZE];
  368.     char tempname[MAXPATHSIZE];
  369. #else
  370.     char * s ;
  371.     char * t ;
  372.     char * p ;
  373.     size_t len ;
  374.     int replacech ;
  375.     char c ;
  376. #endif
  377. #if NEWBUFSIZ > BUFSIZ
  378.     char * inbufptr ;
  379.     char * outbufptr ;
  380. #endif
  381.  
  382.     /* make sure we initialise */
  383.     bakfilename = NULL ;
  384.  
  385.     if (filename != NULL) { /* stdin is not redirected */
  386.  
  387.         /* check for appropriate permissions on the file */
  388.             /* also saves the mode in origfilemode */
  389.         if (checkmode( filename ))
  390.                 return -1 ;
  391.  
  392.         /* we need to create a temporary and backup filename (if */
  393.         /* applicable) in the same directory */
  394.         /* as our file. This is easy to do for DOS since we have the */
  395.         /* _splitpath(), _makepath() functions. */
  396. #if defined(MSDOS)
  397.         _splitpath( filename, drv, dir, fname, NULL );
  398.         _makepath( tempname, drv, dir, MKTEMP_TEMPL, NULL );
  399.         tempfilename = xstrdup( tempname );
  400.         if (!overwrite) {
  401.         _makepath( tempname, drv, dir, fname, BAKEXT );
  402.         if (!strcmp( tempname, filename )) {
  403.             fprintf( stderr, EMSG_BAKFILENAME, filename );
  404.             goto err_freetempfn ;
  405.         }
  406.         bakfilename = xstrdup( tempname );
  407.         }
  408. #else    /* not MSDOS */
  409.         /* check if there is a path prefix */
  410.         if ((s = strrchr( filename, DIRSLASH )) != NULL) {
  411.         c = *++s ;    /* save the character after the slash */
  412.         *s = '\0';
  413.         replacech = 1 ;
  414.         len = strlen( filename ) ;
  415.         t = filename ;
  416.         }
  417.         else {
  418.             replacech = c = 0 ;    /* I init c to suppress the warning */
  419.                     /* issued by gcc -Wall */
  420.             len = sizeof(CURRENTDIR) - 1 ;
  421.             t = CURRENTDIR ;
  422.         }
  423.         /* got to add the extension manually */
  424.         tempfilename = xmalloc( len + sizeof(MKTEMP_TEMPL) );
  425.         strcpy( tempfilename, t ); /* path leading to filename */
  426.         strcat( tempfilename, MKTEMP_TEMPL ); /* filename */
  427.         if (replacech)
  428.         *s = c ;
  429.         if (!overwrite) {
  430.         bakfilename = xmalloc( len + sizeof(BAKEXT) );
  431.         /* copy the filename */
  432.         strcpy( bakfilename, filename );
  433.         /* strip any trailing extension */
  434.         p = bakfilename + strlen( bakfilename ) - 1 ;
  435.         while (p != bakfilename) {
  436.             if (*p == '.') {
  437.                 *p = '\0' ; /* strip current extension */
  438.                 break ;
  439.             }
  440.             if (*p == '/') /* no extension */
  441.                 break ;
  442.             p-- ;
  443.         }
  444.         /* add the new extension */
  445.         strcat( bakfilename, BAKEXT );
  446.         }
  447. #endif
  448.         /* make the temp filename */
  449.         if (mktemp( tempfilename ) == NULL) {
  450.         fprintf( stderr, EMSG_NOTEMPNAME, progname );
  451. err_freebakfn:
  452.         if (!overwrite && bakfilename != NULL)
  453.             free( bakfilename );
  454. #if defined(MSDOS)
  455. err_freetempfn:
  456. #endif
  457.         free( tempfilename );
  458.         tempfilename = NULL ;
  459.         return -1 ;
  460.         }
  461.  
  462.         /* open the filename as the input file */
  463.         if ((infp = fopen( filename, INFILEMODE )) == NULL) {
  464.         fprintf( stderr, EMSG_OPENFILE, progname, filename );
  465.         goto err_freebakfn ;
  466.         }
  467.         /* associate the infilename with the filename for error */
  468.         /* messages */
  469.         infilename = filename ;
  470.  
  471.         /* open the temp file as the output file */
  472.         if ((tempfp = fopen( tempfilename, OUTFILEMODE )) == NULL) {
  473.         fprintf( stderr, EMSG_OPENFILE, progname, tempfilename );
  474.         fclose( infp );
  475.         goto err_freebakfn ;
  476.         }
  477.         outfp = tempfp ;
  478.  
  479.     } /* if filename != NULL */
  480.     else { /* filename == NULL, ie stdin is redirected */
  481.         infp = stdin ;
  482.         outfp = stdout ;
  483.     }
  484.  
  485. #if NEWBUFSIZ > BUFSIZ
  486.     /* make sure that we have a big input and output buffer */
  487.     inbufptr = outbufptr = NULL ;
  488.     /* (don't use xmalloc() because it we can't get what we want, */
  489.     /* we just don't bother, and go ahead with the minimum) */
  490.     if ((inbufptr = malloc( NEWBUFSIZ )) != NULL)
  491.         setvbuf( infp, inbufptr, _IOFBF, NEWBUFSIZ );
  492.     if ((outbufptr = malloc( NEWBUFSIZ )) != NULL)
  493.         setvbuf( outfp, outbufptr, _IOFBF, NEWBUFSIZ );
  494. #endif
  495.     /* do the conversion */
  496.     err = convert( infp, outfp );
  497.  
  498.     /* close the files */
  499.     fclose( infp );
  500.     fclose( outfp );
  501.  
  502.     if (filename == NULL) {
  503.         /* remove the output file handle from the global to avoid */
  504.         /* double attempts to close the same file */
  505.         tempfp = NULL ;
  506.     }
  507.  
  508. #if NEWBUFSIZ > BUFSIZ
  509.     /* got to free buffers we allocated first */
  510.     if (inbufptr != NULL)
  511.         free( inbufptr );
  512.     if (outbufptr != NULL)
  513.         free( outbufptr );
  514. #endif
  515.  
  516.     if (filename != NULL) { /* stdin was not redirected */
  517.  
  518.         if (err) { /* there was an error */
  519.                 /* delete the temp file since we've already created it */
  520.                 remove ( tempfilename );
  521.         goto err_freebakfn ;
  522.         }
  523.  
  524.         /* if we need to back up, delete any backup files of the */
  525.         /* same name first (in case we are running on DOS where */
  526.         /* a rename does not delete the file automatically) */
  527.         if (!overwrite) {
  528.         /* remove existing backup files of same name */
  529.         remove( bakfilename );
  530.         /* rename the original file to the back up name */
  531.         rename( filename, bakfilename );
  532.         }
  533.         else { /* if we do not need to back up delete the original file */
  534. #if defined(MSDOS)
  535.         /* for MSDOS, we need to make sure the file is writeable */
  536.         /* otherwise we cannot delete! Under Unix, this is not */
  537.         /* necessary, since a file can be deleted if we */
  538.         /* have write permissions for the directory */
  539.                 chmod( filename, S_IRUSR|S_IWUSR );
  540. #endif
  541.         remove( filename );
  542.         }
  543.  
  544.         /* rename the temp file to the original file name */
  545.         rename( tempfilename, filename );
  546.  
  547.         /* remove the temp file name from the global for our */
  548.         /* signal handler*/
  549.         tempfilename = NULL ;
  550.  
  551.         /* free memory we allocated */
  552.         if (!overwrite && bakfilename != NULL)
  553.         free( bakfilename );
  554.  
  555.         /* change the file mode to reflect the original file mode */
  556.             chmod( filename, origfilemode );
  557.  
  558.     }    /* stdin was not redirected */
  559.  
  560.     return err ;
  561. }
  562.